/**
 * @file         stm32f411_task_key.c
 * @brief		key task
 * @details	    This file provides all the key task functions to check the user's operation for key.
 * @author		lin jie
 * @date		    2024/6/23
 * @version	    v1.0
 * @par Copyright(c): 	lin jie
 */

#include "stm32f411_task_key.h"
#include "main.h"

#include "FreeRTOS.h"
#include "queue.h"

#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>

#define KEY_RESULT_QUEUE_SIZE 10
#define KEY_EVENT_QUEUE_SIZE 10

static void key_init(void);
static void key_task(void);
static key_result_t key_pfget_result(void);

// typedef enum
// {
//     KEY_IDLE,
//     KEY_TRIGGER,
//     KEY_SHORT_EVENT,
//     KEY_LONG_EVENT,
//     KEY_DOUBLE_EVENT,
// } key_state_t;

struct KeyClass
{
    key_state_t key_state;
    key_result_t key_result;
};

struct KeyClass keyimp[KEY_TYPE_MAX];

static QueueHandle_t g_key_event_queue;
static QueueHandle_t g_key_result_queue;

struct KeyInterface key_interface = {
    .pfinit = key_init,
    .pftask = key_task,
    .pfget_result = key_pfget_result,
};

/**
 * @brief  get key event
 *
 * @param  event key event
 *
 * @retval get event or not
 *         0   not
 *         1   get
 */
static key_result_t key_pfget_result(void)
{
    key_result_t result;
    if (pdTRUE == xQueueReceive(g_key_result_queue, &result, portMAX_DELAY))
        return result;
    else
        return 0;
}

/**
 * @brief  key task loop
 *         use for check user's operation
 *
 * @param  void
 *
 * @retval void
 */
static void key_task(void)
{
    key_result_t result;
    key_event_t event;
    static uint32_t key_triggle_tick;
    static uint8_t short_press_flag;
    // get key event from queue
    if (pdTRUE == xQueueReceive(g_key_event_queue, &event, 200))
    {
        for (key_type_t key = KEY_USER_KEY; key < KEY_TYPE_MAX; key++)
        {
            // search for compare type
            if (KEY_USER_KEY == event.key_type)
            {
                switch (event.state)
                {
                case KEY_PRESS:

                    // if first press key
                    key_triggle_tick = event.triggle_tick;

                    break;
                case KEY_RELEASE:
                    if (event.triggle_tick - key_triggle_tick < 3000)
                    {
                        key_triggle_tick = event.triggle_tick;
                        if (short_press_flag)
                        {
                            // if key is pressed after release in 200ms
                            short_press_flag = 0;
                            result = KEY_DOUBLE_PRESS;
                            printf("double press\r\n");
                            xQueueSend(g_key_result_queue, &result, 100);
                        }
                        else
                        {
                            // delay write result into queue
                            short_press_flag = 1;
                            printf("short press flag triggle\r\n");
                        }
                    }
                    else if (event.triggle_tick - key_triggle_tick > 3000)
                    {
                        key_triggle_tick = event.triggle_tick;
                        // send result to queue
                        result = KEY_LONG_PRESS;
                        printf("long press\r\n");
                        xQueueSend(g_key_result_queue, &result, 100);
                    }

                    break;
                default:

                    break;
                }
            }
        }
    }
    else
    {
        // if key didn't pressed after release in 200ms
        if (short_press_flag)
        {
            // send result to queue
            short_press_flag = 0;
            result = KEY_SHORT_PRESS;
            printf("short press\r\n");
            xQueueSend(g_key_result_queue, &result, 100);
        }
    }
}

/**
 * @brief  key init function
 *
 * @param  key key type
 *
 * @retval key_result   user's operation for key
 */
static void key_init(void)
{
    g_key_event_queue = xQueueCreate(KEY_EVENT_QUEUE_SIZE, sizeof(key_event_t));
    g_key_result_queue = xQueueCreate(KEY_EVENT_QUEUE_SIZE, sizeof(key_result_t));
}

void GPIO_Triggle_set(uint8_t state)
{
    static key_event_t event;
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    // printf("key release\r\n");
    if (state != event.state)
    {
        event.state = state;
        if(KEY_RELEASE == event.state)
        {
            GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
        }
        else if(KEY_PRESS == event.state)
        {
            GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
        }
        else
        {
            
        }
        // write event
        event.key_type = KEY_USER_KEY;
        event.triggle_tick = HAL_GetTick();

        // set EXTI interrupt mode
        GPIO_InitStruct.Pin = USER_KEY_Pin;
        GPIO_InitStruct.Pull = GPIO_PULLUP;
        HAL_GPIO_Init(USER_KEY_GPIO_Port, &GPIO_InitStruct);

        // write event into queue
        xQueueSendFromISR(g_key_event_queue, &event, NULL);

        printf("key trigle : %d\r\n", event.state);
    }
}

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    if (GPIO_Pin == USER_KEY_Pin)
    {
        if(HAL_GPIO_ReadPin(USER_KEY_GPIO_Port, USER_KEY_Pin))
        {
            GPIO_Triggle_set(KEY_RELEASE);
        }
        else
        {
            GPIO_Triggle_set(KEY_PRESS);
        }
        
    }
}
